/*
 * Monahans Micco PMIC Management Routines
 *
 *
 * Copyright (C) 2006, Marvell Corporation(fengwei.yin@marvell.com).
 *
 * This software program is licensed subject to the GNU General Public License
 * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html
 */
#include "xllp_defs.h"
#include "micco.h"
#include "util.h"

#include "mfp.h"

 XLLP_STATUS_T XllpGpioExpanderRead( XLLP_UINT8_T expAddr,
			XLLP_UINT8_T reg,
			XLLP_UINT8_T *pval);
XLLP_STATUS_T XllpGpioExpanderWrite(XLLP_UINT8_T expAddr,
		XLLP_UINT8_T reg,
		XLLP_UINT8_T val);

#define MICCO_REG_NUM		(0xC0)

struct pxa3xx_pmic_regs {
	unsigned int data:8;
	unsigned int hit:1;
	unsigned int mask:1;
};

static struct pxa3xx_pmic_regs micco_regs[MICCO_REG_NUM];

/* Make sure that Power I2C has been initialized before invoke this function */
int micco_write(u8 reg, u8 val);
int micco_read(u8 reg, u8 *pval);

int micco_initchip(void)
{
	memset(&micco_regs, 0, (sizeof(struct pxa3xx_pmic_regs) * MICCO_REG_NUM));
	/* TODO: Mask all micco registers uncacheable now.
	 * We can do some optimization here later.
	 */
	return micco_write(MICCO_SYSCTRL_A, 0xE8);
}

/* Wrap functions for pmic read/write */
int pxa3xx_pmic_read(u8 reg, u8 *pval)
{
	return micco_read(reg, pval);
}

int pxa3xx_pmic_write(u8 reg, u8 val)
{
	return micco_write(reg, val);
}

int micco_read(u8 reg, u8 *pval)
{
	int ret;
	ret = XllpGpioExpanderRead(MICCO_ADDRESS, reg, (XLLP_UINT8_T*)pval);
	if (ret != 0) {
		printf("micco_read error\n");
	}

	return ret;
}


int micco_write(u8 reg, u8 val)
{
	int ret;

	ret = XllpGpioExpanderWrite(MICCO_ADDRESS, reg, val);
	if (ret != 0) {
		printf("micco_write error\n");
	}
	return ret;
}


int i2c_micco_detect()
{
	int chip_id = 0;
	unsigned char *p = (unsigned char *)(&chip_id);
	int ret ;
	ret = XllpGpioExpanderRead(MICCO_ADDRESS, MICCO_CHIP_ID, (XLLP_UINT8_T*)p);
	if (chip_id < 0){
		printf("micco unavailable!id:%d ret = %d\n", chip_id, ret);

	} else {
		printf("micco(chip id:0x%02x) detected.\n", chip_id);
	}

        return 0;
}

/* Micco Audio functions */
static volatile u8 micco_audio_regs[MICCO_AUDIO_REGS_NUM];

int micco_codec_read(u8 reg, u8 *val)
{
	return micco_read(MICCO_AUDIO_REG_BASE + reg, val);
}

int micco_codec_write(u8 reg, u8 val)
{
	return micco_write(MICCO_AUDIO_REG_BASE + reg, val);
}

void static micco_read_codec(void)
{
	int i;
	u8 val;

	for (i = 0; i < MICCO_AUDIO_REGS_NUM; i++) {
		micco_codec_read(i, &val);
		micco_audio_regs[i] = val;
	}
}

void micco_dump_codec(void)
{
	int i;
	micco_read_codec();

	for (i = 0; i < MICCO_AUDIO_REGS_NUM; i++) {
		printf("Micco_audio_reg[%d] = 0x%x\n",  i, micco_audio_regs[i]);
	}
}

int micco_audio_init(void)
{
	int i;

	/* The default setting for Micco */
	micco_audio_regs[MICCO_MUX_MONO] = 0x00; /* MONO */
	micco_audio_regs[MICCO_MUX_BEAR] = 0x00; /* BEAR */
	micco_audio_regs[MICCO_MUX_LINE_OUT] = 0x00; /* LINE OUT */
	micco_audio_regs[MICCO_MUX_STEREO_CH1] =
		0x00;	/* STEREO_CH1 */
	micco_audio_regs[MICCO_MUX_STEREO_CH2] =
		0x00;	/* STEREO_CH2 */

	micco_audio_regs[MICCO_AUDIO_LINE_AMP] =
		MICCO_AUDIO_LINE_AMP_EN;	/* Enable the Line AMP */

	/* Gain for both channel controlled separately. Enable Setero */
	micco_audio_regs[MICCO_STEREO_AMPLITUDE_CH1] =
		MICCO_STEREO_GAIN_SEPARATE | MICCO_STEREO_EN;

	/* Soft startup of the Stereo amplifiers */
	micco_audio_regs[MICCO_STEREO_AMPLITUDE_CH2] = 0x0;

	micco_audio_regs[MICCO_HIFI_DAC_CONTROL] =
		MICCO_HIFI_DAC_ON;

	micco_audio_regs[MICCO_MONO_VOL] =
		MICCO_MONO_EN | 0xa;
	micco_audio_regs[MICCO_BEAR_VOL] =
		MICCO_BEAR_EN | 0xa;

	/* Micco as I2S slave. Use I2S MSB normal mode */
	micco_audio_regs[MICCO_I2S_CONTROL] =
		MICCO_I2S_MSB_JU_MODE;

	micco_audio_regs[MICCO_TX_PGA] =
		0x0c;	/* 0 dB */
	micco_audio_regs[MICCO_MIC_PGA] =
		MICCO_MIC_PGA_EXT_EN | MICCO_MIC_PGA_INT_EN |
		MICCO_MIC_PGA_SELMIC_2 | MICCO_MIC_PGA_AMP_EN |
		0x7;	/* 30 dB*/

	micco_audio_regs[MICCO_TX_PGA_MUX] =
		0xFF;

	micco_audio_regs[MICCO_VCODEC_ADC_CONTROL] =
		MICCO_VCODEC_ADC_ON_EN | 0x08;
	/* PCM_SDI normal operation, PCM_SDO enabled */
	micco_audio_regs[MICCO_VCODEC_VDAC_CONTROL] =
		MICCO_VDAC_ON | MICCO_VDAC_HPF_BYPASS;

	micco_audio_regs[MICCO_SIDETONE] =
		MICCO_SIDETONE_EN | MICCO_SIDETONE_GAIN_STEREO | 0x08;

	/* Enable AUX1,2. AUX1, 2 gain 0dB */
	micco_audio_regs[MICCO_PGA_AUX1_2] =
		MICCO_PGA_AUX1_EN | MICCO_PGA_AUX2_EN;

	/* Enable AUX3. AUX3 gain 0 dB */
	micco_audio_regs[MICCO_PGA_AUX3] =
		MICCO_PGA_AUX3_EN;

	/* DAC1, 2, 3 gain 0dB */
	micco_audio_regs[MICCO_PGA_DACS] =
		0x00;

	/*Soft start for MONO, BEAR LINE and STEREO is 61.5ms */
	micco_audio_regs[MICCO_SOFT_START_RAMP] =
		0x00;

	for (i = 0; i < MICCO_AUDIO_REGS_NUM; i++)
		micco_codec_write(i, micco_audio_regs[i]);

	return 0;
}
/* FIXME: The Stereo have left and right channel. Need add it later */
int micco_codec_enable_output(int type)
{
	switch (type) {
	case CODEC_BEAR:
//		if (!(micco_audio_regs[MICCO_BEAR_VOL] & MICCO_BEAR_EN)) {
			micco_audio_regs[MICCO_BEAR_VOL] |=  MICCO_BEAR_EN;
			printf("enable codec_bear output\n");
			micco_codec_write(MICCO_BEAR_VOL,
				 micco_audio_regs[MICCO_BEAR_VOL]);
	//	}
		break;

	case CODEC_MONO:
	//	if (!(micco_audio_regs[MICCO_MONO_VOL] & MICCO_MONO_EN)) {
			micco_audio_regs[MICCO_MONO_VOL] |= MICCO_MONO_EN;
			printf("enable codec_mono output\n");
			micco_codec_write(MICCO_MONO_VOL,
				micco_audio_regs[MICCO_MONO_VOL]);
	//	}
		break;

	case CODEC_STEREO:
//		if (!(micco_audio_regs[MICCO_STEREO_AMPLITUDE_CH1] &
	//			MICCO_STEREO_EN)){
			printf("enable codec_stereo output\n");
			micco_audio_regs[MICCO_STEREO_AMPLITUDE_CH1] |=
				MICCO_STEREO_EN;
			micco_codec_write(MICCO_STEREO_AMPLITUDE_CH1,
				micco_audio_regs[MICCO_STEREO_AMPLITUDE_CH1]);
//		}
		break;

	case CODEC_LINE_OUT:
		if (!(micco_audio_regs[MICCO_AUDIO_LINE_AMP] &
				MICCO_AUDIO_LINE_AMP_EN)) {
			micco_audio_regs[MICCO_AUDIO_LINE_AMP] |=
				MICCO_AUDIO_LINE_AMP_EN;
			printf("enable codec_line out output\n");
			micco_codec_write(MICCO_AUDIO_LINE_AMP,
				micco_audio_regs[MICCO_AUDIO_LINE_AMP]);
		}
		break;

	case CODEC_ADC:
//		if (!(micco_audio_regs[MICCO_VCODEC_ADC_CONTROL] &
//				MICCO_VCODEC_ADC_ON_EN)) {
			micco_audio_regs[MICCO_VCODEC_ADC_CONTROL] |=
				MICCO_VCODEC_ADC_ON_EN;
			printf("enable codec_adc output\n");
			micco_codec_write(MICCO_VCODEC_ADC_CONTROL,
				micco_audio_regs[MICCO_VCODEC_ADC_CONTROL]);
//		}
		break;
	default:
		return -1;
	}

	return 0;
}

int micco_codec_disable_output(int type)
{
	switch (type) {
	case CODEC_BEAR:
		if (micco_audio_regs[MICCO_BEAR_VOL] & MICCO_BEAR_EN) {
			micco_audio_regs[MICCO_BEAR_VOL] &=  ~MICCO_BEAR_EN;
			micco_codec_write(MICCO_BEAR_VOL,
				 micco_audio_regs[MICCO_BEAR_VOL]);
		}
		break;

	case CODEC_MONO:
		if (micco_audio_regs[MICCO_MONO_VOL] & MICCO_MONO_EN) {
			micco_audio_regs[MICCO_MONO_VOL] &= ~MICCO_MONO_EN;
			micco_codec_write(MICCO_MONO_VOL,
				micco_audio_regs[MICCO_MONO_VOL]);
		}
		break;

	case CODEC_STEREO:
		if (micco_audio_regs[MICCO_STEREO_AMPLITUDE_CH1] &
				 MICCO_STEREO_EN){
			micco_audio_regs[MICCO_STEREO_AMPLITUDE_CH1] &=
				~MICCO_STEREO_EN;
			micco_codec_write(MICCO_STEREO_AMPLITUDE_CH1,
				micco_audio_regs[MICCO_STEREO_AMPLITUDE_CH1]);
		}
		break;

	case CODEC_LINE_OUT:
		if (micco_audio_regs[MICCO_AUDIO_LINE_AMP] &
				MICCO_AUDIO_LINE_AMP_EN) {
			micco_audio_regs[MICCO_AUDIO_LINE_AMP] &=
				~MICCO_AUDIO_LINE_AMP_EN;
			micco_codec_write(MICCO_AUDIO_LINE_AMP,
				micco_audio_regs[MICCO_AUDIO_LINE_AMP]);
		}
		break;

	case CODEC_ADC:
		if (micco_audio_regs[MICCO_VCODEC_ADC_CONTROL] &
				MICCO_VCODEC_ADC_ON_EN) {
			micco_audio_regs[MICCO_VCODEC_ADC_CONTROL] &=
				~MICCO_VCODEC_ADC_ON_EN;
			micco_codec_write(MICCO_VCODEC_ADC_CONTROL,
				micco_audio_regs[MICCO_VCODEC_ADC_CONTROL]);
		}
		break;
	default:
		return -1;
	}

	return 0;
}

/* We don't check the paramater. The caller need make sure
 * that the vol is in legal range.
 */
int micco_codec_set_output_vol(int type, int vol)
{
	switch (type) {
	case CODEC_BEAR:
		micco_audio_regs[MICCO_BEAR_VOL] = (~0x3F &
			micco_audio_regs[MICCO_BEAR_VOL]) | vol;
		micco_codec_write(MICCO_BEAR_VOL,
			micco_audio_regs[MICCO_BEAR_VOL]);
		break;

	case CODEC_MONO:
		micco_audio_regs[MICCO_MONO_VOL] = (~0x3F &
			micco_audio_regs[MICCO_MONO_VOL]) | vol;
		micco_codec_write(MICCO_MONO_VOL,
			micco_audio_regs[MICCO_MONO_VOL]);

	case CODEC_STEREO:
		micco_audio_regs[MICCO_STEREO_AMPLITUDE_CH1] =
			(~0x3F & micco_audio_regs[MICCO_STEREO_AMPLITUDE_CH1]) |
			vol;
		micco_codec_write(MICCO_STEREO_AMPLITUDE_CH1,
			micco_audio_regs[MICCO_STEREO_AMPLITUDE_CH1]);
		break;

	case CODEC_LINE_OUT:
		micco_audio_regs[MICCO_AUDIO_LINE_AMP] =
			(~0x0F & micco_audio_regs[MICCO_AUDIO_LINE_AMP]) |
			vol;
		micco_codec_write(MICCO_AUDIO_LINE_AMP,
			micco_audio_regs[MICCO_AUDIO_LINE_AMP]);
		break;
	default:
		return -1;
	}

	return 0;
}


int micco_codec_enable_input(int type)
{
	switch (type) {
	case CODEC_AUX1:
		if (!(micco_audio_regs[MICCO_PGA_AUX1_2] & MICCO_PGA_AUX1_EN)) {
			micco_audio_regs[MICCO_PGA_AUX1_2] |= MICCO_PGA_AUX1_EN;
			micco_codec_write(MICCO_PGA_AUX1_2,
				micco_audio_regs[MICCO_PGA_AUX1_2]);
		}
		break;

	case CODEC_AUX2:
		if (!(micco_audio_regs[MICCO_PGA_AUX1_2] & MICCO_PGA_AUX2_EN)) {
			micco_audio_regs[MICCO_PGA_AUX1_2] |= MICCO_PGA_AUX2_EN;
			micco_codec_write(MICCO_PGA_AUX1_2,
				micco_audio_regs[MICCO_PGA_AUX1_2]);
		}
		break;

	case CODEC_AUX3:
		if (!(micco_audio_regs[MICCO_PGA_AUX3] & MICCO_PGA_AUX3_EN)) {
			micco_audio_regs[MICCO_PGA_AUX3] |= MICCO_PGA_AUX3_EN;
			micco_codec_write(MICCO_PGA_AUX3,
				micco_audio_regs[MICCO_PGA_AUX3]);
		}
		break;

	case CODEC_MIC1:
		micco_audio_regs[MICCO_MIC_PGA] &= ~MICCO_MIC_PGA_SELMIC_2;
		micco_audio_regs[MICCO_MIC_PGA] =
		       micco_audio_regs[MICCO_MIC_PGA] |
		       MICCO_MIC_PGA_EXT_EN | MICCO_MIC_PGA_INT_EN |
		       MICCO_MIC_PGA_AMP_EN;
		micco_codec_write(MICCO_MIC_PGA,
			micco_audio_regs[MICCO_MIC_PGA]);
		break;

	case CODEC_MIC2:
		micco_audio_regs[MICCO_MIC_PGA] |= MICCO_MIC_PGA_SELMIC_2;
		micco_audio_regs[MICCO_MIC_PGA] =
		       micco_audio_regs[MICCO_MIC_PGA] |
		       MICCO_MIC_PGA_EXT_EN | MICCO_MIC_PGA_INT_EN |
		       MICCO_MIC_PGA_AMP_EN;
		micco_codec_write(MICCO_MIC_PGA,
			micco_audio_regs[MICCO_MIC_PGA]);
		break;

	case CODEC_PCM:
		if (!(micco_audio_regs[MICCO_VCODEC_VDAC_CONTROL] &
				MICCO_VDAC_ON)) {
			micco_audio_regs[MICCO_VCODEC_VDAC_CONTROL] |=
				MICCO_VDAC_ON;
			micco_audio_regs[MICCO_VCODEC_VDAC_CONTROL] &=
				~MICCO_VDAC_HPF_MUTE;
			micco_codec_write(MICCO_VCODEC_VDAC_CONTROL,
				micco_audio_regs[MICCO_VCODEC_VDAC_CONTROL]);
		}
		break;

	case CODEC_HIFI:
		if (!(micco_audio_regs[MICCO_HIFI_DAC_CONTROL] &
				MICCO_HIFI_DAC_ON)) {
			micco_audio_regs[MICCO_HIFI_DAC_CONTROL] |=
				MICCO_HIFI_DAC_ON;
			micco_codec_write(MICCO_HIFI_DAC_CONTROL,
				micco_audio_regs[MICCO_HIFI_DAC_CONTROL]);
		}
		break;

	default:
		return -1;
	}

	return 0;
}

int micco_codec_disable_input(int type)
{
	switch (type) {
	case CODEC_AUX1:
		if (micco_audio_regs[MICCO_PGA_AUX1_2] & MICCO_PGA_AUX1_EN) {
			micco_audio_regs[MICCO_PGA_AUX1_2] &=
				~MICCO_PGA_AUX1_EN;
			micco_codec_write(MICCO_PGA_AUX1_2,
				micco_audio_regs[MICCO_PGA_AUX1_2]);
		}
		break;

	case CODEC_AUX2:
		if (micco_audio_regs[MICCO_PGA_AUX1_2] & MICCO_PGA_AUX2_EN) {
			micco_audio_regs[MICCO_PGA_AUX1_2] &=
				~MICCO_PGA_AUX2_EN;
			micco_codec_write(MICCO_PGA_AUX1_2,
				micco_audio_regs[MICCO_PGA_AUX1_2]);
		}
		break;

	case CODEC_AUX3:
		if (micco_audio_regs[MICCO_PGA_AUX3] & MICCO_PGA_AUX3_EN) {
			micco_audio_regs[MICCO_PGA_AUX3] &= ~MICCO_PGA_AUX3_EN;
			micco_codec_write(MICCO_PGA_AUX3,
				micco_audio_regs[MICCO_PGA_AUX3]);
		}
		break;

	case CODEC_MIC1:
		micco_audio_regs[MICCO_MIC_PGA] =
			micco_audio_regs[MICCO_MIC_PGA] &
			~(MICCO_MIC_PGA_EXT_EN | MICCO_MIC_PGA_INT_EN |
				MICCO_MIC_PGA_AMP_EN);
		micco_codec_write(MICCO_MIC_PGA,
				micco_audio_regs[MICCO_MIC_PGA]);
		break;

	case CODEC_MIC2:
		micco_audio_regs[MICCO_MIC_PGA] =
			micco_audio_regs[MICCO_MIC_PGA] &
			~(MICCO_MIC_PGA_EXT_EN | MICCO_MIC_PGA_INT_EN |
				MICCO_MIC_PGA_AMP_EN);
		micco_codec_write(MICCO_MIC_PGA,
				micco_audio_regs[MICCO_MIC_PGA]);
		break;

	case CODEC_PCM:
		if (micco_audio_regs[MICCO_VCODEC_VDAC_CONTROL] &
				MICCO_VDAC_ON) {
			micco_audio_regs[MICCO_VCODEC_VDAC_CONTROL] &=
				~MICCO_VDAC_ON;
			micco_codec_write(MICCO_VCODEC_VDAC_CONTROL,
				micco_audio_regs[MICCO_VCODEC_VDAC_CONTROL]);
		}
		break;

	case CODEC_HIFI:
		if (micco_audio_regs[MICCO_HIFI_DAC_CONTROL] &
				MICCO_HIFI_DAC_ON) {
			micco_audio_regs[MICCO_HIFI_DAC_CONTROL] &=
				~MICCO_HIFI_DAC_ON;
			micco_codec_write(MICCO_HIFI_DAC_CONTROL,
				micco_audio_regs[MICCO_HIFI_DAC_CONTROL]);
		}
		break;

	default:
		return -1;
	}
	return 0;
}

/* We don't check the paramater. The caller need make sure
 * that the vol is in legal range.
 */
int micco_codec_set_input_gain(int type, int gain)
{
	switch (type) {
	case CODEC_AUX1:
		micco_audio_regs[MICCO_PGA_AUX1_2] = ((~0x03 &
			micco_audio_regs[MICCO_PGA_AUX1_2]) | gain);
		micco_codec_write(MICCO_PGA_AUX1_2,
			micco_audio_regs[MICCO_PGA_AUX1_2]);
		break;

	case CODEC_AUX2:
		micco_audio_regs[MICCO_PGA_AUX1_2] = ((~0x30 &
			micco_audio_regs[MICCO_PGA_AUX1_2]) | (gain << 4));
		micco_codec_write(MICCO_PGA_AUX1_2,
			micco_audio_regs[MICCO_PGA_AUX1_2]);
		break;

	case CODEC_AUX3:
		micco_audio_regs[MICCO_PGA_AUX3] = ((~0x03 &
			micco_audio_regs[MICCO_PGA_AUX3]) | gain);
		micco_codec_write(MICCO_PGA_AUX3,
			micco_audio_regs[MICCO_PGA_AUX3]);
		break;

	case CODEC_MIC1:
	case CODEC_MIC2:
		micco_audio_regs[MICCO_MIC_PGA] = ((~0x07 &
			micco_audio_regs[MICCO_MIC_PGA]) | gain);
		micco_codec_write(MICCO_MIC_PGA,
			micco_audio_regs[MICCO_MIC_PGA]);
		break;

	case CODEC_PCM:	/* Need check whether HIFI and PCM support input? */
	case CODEC_HIFI:
		break;
	default:
		return -1;
	}

	return 0;
}

int micco_codec_set_sample_rate(int port, int rate)
{
	switch (port) {
	case MICCO_VOICE_PORT:
		switch (rate) {
		case 8000:
			micco_audio_regs[MICCO_VCODEC_ADC_CONTROL] =
				(~(0x03 << 3) &
				 micco_audio_regs[MICCO_VCODEC_ADC_CONTROL]);
			break;

		case 16000:
			micco_audio_regs[MICCO_VCODEC_ADC_CONTROL] =
				(~(0x03 << 3) &
				 micco_audio_regs[MICCO_VCODEC_ADC_CONTROL]) |
				(0x01 << 3);
			break;

		case 32000:
			micco_audio_regs[MICCO_VCODEC_ADC_CONTROL] |=(0x3 << 3);
			break;
		default:
			return -1;
		}
		micco_codec_write(MICCO_VCODEC_ADC_CONTROL,
			micco_audio_regs[MICCO_VCODEC_ADC_CONTROL]);
		break;

	case MICCO_HIFI_PORT:
		switch (rate) {
		case 8000:
			micco_audio_regs[MICCO_I2S_CONTROL] &= 0xF0;
			break;
		case 11025:
			micco_audio_regs[MICCO_I2S_CONTROL] &= 0xF0;
			micco_audio_regs[MICCO_I2S_CONTROL] |= 0x01;
			break;
		case 12000:
			micco_audio_regs[MICCO_I2S_CONTROL] &= 0xF0;
			micco_audio_regs[MICCO_I2S_CONTROL] |= 0x02;
			break;
		case 16000:
			micco_audio_regs[MICCO_I2S_CONTROL] &= 0xF0;
			micco_audio_regs[MICCO_I2S_CONTROL] |= 0x03;
			break;
		case 22050:
			micco_audio_regs[MICCO_I2S_CONTROL] &= 0xF0;
			micco_audio_regs[MICCO_I2S_CONTROL] |= 0x04;
			break;
		case 24000:
			micco_audio_regs[MICCO_I2S_CONTROL] &= 0xF0;
			micco_audio_regs[MICCO_I2S_CONTROL] |= 0x05;
			break;
		case 32000:
			micco_audio_regs[MICCO_I2S_CONTROL] &= 0xF0;
			micco_audio_regs[MICCO_I2S_CONTROL] |= 0x06;
			break;
		case 44100:
			micco_audio_regs[MICCO_I2S_CONTROL] &= 0xF0;
			micco_audio_regs[MICCO_I2S_CONTROL] |= 0x07;
			break;
		case 48000:
			micco_audio_regs[MICCO_I2S_CONTROL] &= 0xF0;
			micco_audio_regs[MICCO_I2S_CONTROL] |= 0x0F;
			break;
		default:
			return -1;
		}
		micco_codec_write(MICCO_I2S_CONTROL,
			micco_audio_regs[MICCO_I2S_CONTROL]);
		break;
	default:
		return -1;
	}

	return 0;
}

int micco_sidetone_enable(void)
{
	if (!(micco_audio_regs[MICCO_SIDETONE] & MICCO_SIDETONE_EN)) {
		micco_audio_regs[MICCO_SIDETONE] |= MICCO_SIDETONE_EN;
		micco_codec_write(MICCO_SIDETONE,
			micco_audio_regs[MICCO_SIDETONE]);
	}

	return 0;
}

int micco_sidetone_disable(void)
{
	if (micco_audio_regs[MICCO_SIDETONE] & MICCO_SIDETONE_EN) {
		micco_audio_regs[MICCO_SIDETONE] &= ~MICCO_SIDETONE_EN;
		micco_codec_write(MICCO_SIDETONE,
			micco_audio_regs[MICCO_SIDETONE]);
	}

	return 0;
}

#define	MFP_PIN_GPIO18		((0x02B8 << 16) | (18))
#define	MFP_PMIC_INT				(MFP_PIN_GPIO18)
#define MFP_DS03X		(2)
#define MFP_LPM_PULL_HIGH     	0x7
#define MFP_EDGE_NONE		0x0
#define MFP_AF0			(0)

struct pxa3xx_pin_config lt_micco_pins[] = {
	PXA3xx_MFP_CFG("Micco INT", MFP_PMIC_INT, MFP_AF0, MFP_DS03X, 0, MFP_LPM_PULL_HIGH, MFP_EDGE_NONE),
};

#define MFP2GPIO(mfp) ((mfp) & 0xFFFF)
#define GPIO_DIR_IN		0
#define GPIO_DIR_OUT		1
#define __REG(x)	(*((volatile u32 *)(x)))

# define __REG2(x,y)	\
	(*(volatile u32 *)((u32)&__REG(x) + (y)))
#define GSDR(x)		__REG2(0x40E00400, ((x) & 0x60) >> 3)
#define GCDR(x)		__REG2(0x40300420, ((x) & 0x60) >> 3)

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

int pxa3xx_gpio_set_direction(int gpio_id, int dir)
{
	int gpio = MFP2GPIO(gpio_id);

	if (dir == GPIO_DIR_IN)
		GCDR(gpio) = 1u << (gpio & 0x1f);
	else
		GSDR(gpio) = 1u << (gpio & 0x1f);

	return 0;
}
/* Initialization functions for system */
/* LDO12 enabled for IO */
void micco_enable_LDO12(void)
{
	u8 val;

	micco_read(MICCO_LDO1312, &val);
	val = (0x0F & val);
	micco_write(MICCO_LDO1312, val);

	return;
}

 int micco_probe()
{
	int ret;
	u8 value;

	ret = micco_initchip();
	if (ret != 0) {
		printf("Initialize Micco failed with ret 0x%x\n", ret);
	}

	pxa3xx_mfp_set_configs(lt_micco_pins, ARRAY_SIZE(lt_micco_pins));
	pxa3xx_gpio_set_direction(MFP_PMIC_INT, GPIO_DIR_IN);

	/* Mask interrupts that are not needed */
	micco_write(MICCO_IRQ_MASK_A, 0xFE);
	micco_write(MICCO_IRQ_MASK_B, 0xFF);
	micco_write(MICCO_IRQ_MASK_C, 0xFF);
	micco_write(MICCO_IRQ_MASK_D, 0xFF);

	/* avoid SRAM power off during sleep*/
	micco_write(0x10, 0x05);
	micco_write(0x11, 0xff);
	micco_write(0x12, 0xff);

	/* Enable the ONKEY power down functionality */
	micco_write(MICCO_SYSCTRL_B, 0x20);
	micco_write(MICCO_SYSCTRL_A, 0x60);

	/* IRQ is masked during the power-up sequence and will not be released
	 * until they have been read for the first time */
	micco_read(MICCO_EVENT_A, &value);
	micco_read(MICCO_EVENT_B, &value);
	micco_read(MICCO_EVENT_C, &value);
	micco_read(MICCO_EVENT_D, &value);

	micco_enable_LDO12();


	return 0;
}
